package com.huawei.esdk.tp.authorize;

import java.util.Date;

import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.LoggingMessage;
import org.apache.log4j.Logger;

import com.huawei.esdk.platform.authorize.itf.AbstractAuthorizeAdapter;
import com.huawei.esdk.platform.common.MessageContext;
import com.huawei.esdk.platform.common.ThreadLocalHolder;
import com.huawei.esdk.platform.common.bean.log.InterfaceLogBean;
import com.huawei.esdk.platform.common.constants.ESDKErrorCodeConstant;
import com.huawei.esdk.platform.common.utils.ApplicationContextUtil;
import com.huawei.esdk.platform.common.utils.StringUtils;
import com.huawei.esdk.platform.log.itf.IInterfaceLog;
import com.huawei.esdk.tp.common.ErrInfo;
import com.huawei.esdk.tp.common.constants.InfoKeys;

public class RandomChallengeAuthorize extends AbstractAuthorizeAdapter
{
    private static final Logger LOGGER = Logger.getLogger(RandomChallengeAuthorize.class);
    
    public RandomChallengeAuthorize(String businessName)
    {
        super(businessName);
    }
    
    @Override
    public boolean needAuthorize(String funcName, Object message)
    {
        boolean result;
        //SOAPAction "Authenticate" need authenticate base on the session id so in the true clause
        if (funcName.contains("LoginRequest"))
        {
            SoapMessage soapMessage = (SoapMessage)message;
            if (null != soapMessage)
            {
                HttpServletRequest req = (HttpServletRequest)soapMessage.get("HTTP.REQUEST");
                String sessionId = req.getSession().getId();
                getMessageContext().getEntities().put(InfoKeys.SDK_SESSION_ID.toString(), sessionId);
                
                String soapAction = req.getHeader("SOAPAction");
                logOperation(soapMessage, req, soapAction);
            }
            result = false;
        }
        else
        {
            result = true;
        }
        
        LOGGER.debug("needAuthorize result:" + result);
        return result;
    }
    
    private MessageContext getMessageContext()
    {
        MessageContext mc = ThreadLocalHolder.get();
        if (mc == null)
        {
            mc = new MessageContext();
            ThreadLocalHolder.set(mc);
        }
        
        return mc;
    }
    
    @Override
    public String authorize(Object message)
    {
        SoapMessage soapMessage = (SoapMessage)message;
        if (null == soapMessage)
        {
            return Integer.toString(ESDKErrorCodeConstant.ERROR_CODE_SDK_AUTHORIZE_FAILURE);
        }
        HttpServletRequest req = (HttpServletRequest)soapMessage.get("HTTP.REQUEST");
        
        MessageContext mc = getMessageContext();
        String soapAction = req.getHeader("SOAPAction");
        if (StringUtils.isEmpty(soapAction))
        {
            SOAPException soapExc = new SOAPException("soapAction can not be null");
            Fault fault = new Fault(soapExc);
            fault.setFaultCode(new QName(String.valueOf(ErrInfo.SESSIONID_ERRORCODE)));
            throw fault;
        }
        
        String sessionId = req.getSession().getId();
        SessionManager sessionMgr = SessionManager.getInstance();
        
        if (soapAction.contains("Authenticate"))
        {
            //Just need check the session exists
            if (!sessionMgr.isSDKSessionExists(sessionId))
            {
                // SessionID is not correct: it's not the session from LoginRequest method
                SOAPException soapExc = new SOAPException("");
                Fault fault = new Fault(soapExc);
                fault.setFaultCode(new QName(String.valueOf(ErrInfo.AUTHORIZE_ERRORCODE)));
                throw fault;
            }
            
            sessionId = renewSession(req, sessionId, mc);
        }
        else
        {
            //Need check the session exists and has logged in
            if (!sessionMgr.isSDKSessionExists(sessionId) || !sessionMgr.getSDKSession(sessionId).isLogged())
            {
                SOAPException soapExc = new SOAPException("");
                Fault fault = new Fault(soapExc);
                fault.setFaultCode(new QName(String.valueOf(ErrInfo.AUTHORIZE_ERRORCODE)));
                throw fault;
            }
            
        }
        logOperation(soapMessage, req, soapAction);
        
        mc.getEntities().put(InfoKeys.SDK_SESSION_ID.toString(), sessionId);
        return "0";
    }
    
    private void logOperation(SoapMessage soapMessage, HttpServletRequest req, String soapAction)
    {
        if (!StringUtils.isEmpty(soapAction))
        {
            String interfaceName = soapAction.substring(soapAction.lastIndexOf("/") + 1, soapAction.length() - 1);
            if (!soapAction.equals("KeepAlive"))
            {
                String messageId = (String)soapMessage.getExchange().get(LoggingMessage.ID_KEY);
                if (messageId == null)
                {
                    messageId = LoggingMessage.nextId();
                    soapMessage.getExchange().put(LoggingMessage.ID_KEY, messageId);
                }
                
                
                InterfaceLogBean bean = new InterfaceLogBean();
                bean.setTransactionId(messageId);
                bean.setProduct("TP");
                bean.setInterfaceType("1");
                bean.setProtocolType("SOAP");
                bean.setReq(true);
                bean.setName(interfaceName);
                bean.setSourceAddr(req.getRemoteHost());
                bean.setTargetAddr(req.getLocalAddr());
                bean.setReqTime(new Date());
                
                IInterfaceLog logger = ApplicationContextUtil.getBean("interfaceLogger");
                logger.info(bean);
            }
        }
    }
    
    /**
     * Renew the session for security purpose
     * 
     * @param req HttpServletRequest
     * @param oldSessionId The session ID generated in the LoginRequest
     * @since eSDK V001R003C10
     */
    private String renewSession(HttpServletRequest req, String oldSessionId, MessageContext mc)
    {
        //Retrieve the session info from the manager first
        SessionManager sessionMgr = SessionManager.getInstance();
        SessionInfo info = sessionMgr.getSDKSession(oldSessionId);
        
        //Then renew the session, when the invalidate method is called the session listener
        //will be called also which will call the sessionMgr.removeSDKSession(sessionId)
        req.getSession().invalidate();
        //after the invalidate method is called, the variables stored in the thread local may be removed
        //so need store it again here
        ThreadLocalHolder.set(mc);
        
        String newSDKSessionId = req.getSession().getId();
        
        sessionMgr.saveSDKSession(newSDKSessionId, info);
        
        return newSDKSessionId;
    }
    
    @Override
    public boolean reqMsgMatchesBuiness(String funcName, Object message)
    {
        if (StringUtils.isEmpty(funcName))
        {
            return false;
        }
        
        if (funcName.contains("://smc.huawei.com/"))
        {
            return true;
        }
        
        return false;
    }
}
